package com.summer.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.summer.common.R;
import com.summer.entity.Category;
import com.summer.entity.Dish;
import com.summer.entity.DishDto;
import com.summer.entity.DishFlavor;
import com.summer.service.CategoryService;
import com.summer.service.DishFlavorService;
import com.summer.service.DishService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/dish")
@Slf4j
public class DishController {

    @Autowired
    private DishService dishService;

    @Autowired
    private CategoryService categoryService;

    @Autowired
    private DishFlavorService dishFlavorService;

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 添加菜品与菜品的口味 (双表操作)
     * @param dishDto 接收前端菜品的json数据
     * @return 返回添加成功信息（字符串）
     */
    @PostMapping
    public R<String> addDishAndFlavor(@RequestBody DishDto dishDto){

        //优化：添加菜品时删除redis缓存
        String key = "dish_" + dishDto.getCategoryId();
        redisTemplate.delete(key);

        log.info("添加菜品请求，请求参数信息：dishDto={}", dishDto.toString());
        // TODO 需要事务处理，且业务复杂，满足在业务层创建方法处理。
        // 调用业务层创建的方法
        dishService.addDishAndFlavor(dishDto);
        // 返回添加成功信息
        return R.success("添加成功！");
    }

    // TODO 此方法本可以用多表查询，只需要查询一次。但是因为其使用了MP技术，自己定义sql要封装一个DTO对象返回total总页数与分页数据
    //  （因为不能使用mybatis的分页插件，会与MP分页拦截器冲突），更加麻烦。
    /**
     * 分页条件查询
     * @param page 当前页
     * @param pageSize 每页显示数
     * @param name 模糊查询条件
     * @return 返回分页数据
     */
    @GetMapping("/page")
    public R<Page<DishDto>> getByPageAndCondition(long page, long pageSize, String name){
        log.info("dish的分页条件查询请求：page="+page+"，pageSize="+pageSize+", name="+name);
        // 1. 条件构造器
        LambdaQueryWrapper<Dish> lqw = new LambdaQueryWrapper<>();
        lqw.like(name != null, Dish::getName, name);
        lqw.orderByAsc(Dish::getPrice).orderByAsc(Dish::getUpdateTime);

        // 2. 分页查询API
        Page<Dish> dishPage = new Page<>(page, pageSize);

        // 3. 调用Service分页查询
        dishService.page(dishPage, lqw);

        // 4. 封装DishDto
        Page<DishDto> dishDtoPage = new Page<>();

        // 4.1 将dishPage除了records的其他属性拷贝给dishDtoPage
        BeanUtils.copyProperties(dishPage, dishDtoPage, "records"); // TODO 拷贝工具类

        // 4.2 处理dtoPage的records：将List<Dish>  -->  List<DishDto>
        List<Dish> dishPageRecords = dishPage.getRecords();
        List<DishDto> dishDtoList = new ArrayList<>();
        // 4.2.1 遍历List中的每个dish，将dish copy给dishDot
        for (Dish dish : dishPageRecords) {
            DishDto dishDto = new DishDto();
            BeanUtils.copyProperties(dish, dishDto);
            // 4.2.2 为dishDto的categoryName赋值
            Category category = categoryService.getById(dish.getCategoryId());
            if (category != null) {
                dishDto.setCategoryName(category.getName());
            }
            // 4.2.3 将dishDto加入列表
            dishDtoList.add(dishDto);
        }
        // 4.2.4
        dishDtoPage.setRecords(dishDtoList);
        // 5. 返回分页数据
        return R.success(dishDtoPage);
    }

    /**
     * 根据id获取dish的DIishDto对象并返回前端
     * @param id dish的Id
     * @return 返回DishDto对象
     */
    @GetMapping("/{id}")
    public R<DishDto> getDishById(@PathVariable long id){
        log.info("根据id查询dish请求，并封装到Dto，id={}", id);
        //1. 封装DishDto
        DishDto dishDto = new DishDto();
        //1.1 设置dish数据：调用dishService方法根据id获取dish，并将它copy给dishDTO
        Dish dish = dishService.getById(id);
        BeanUtils.copyProperties(dish, dishDto);

        //1.2 设置flavor数据：调用dishFlavorService根据dishId获取全部数据方法（list(lqw)）
        LambdaQueryWrapper<DishFlavor> lqw = new LambdaQueryWrapper<>();
        lqw.eq(DishFlavor::getDishId, id);
        List<DishFlavor> dishFlavorList = dishFlavorService.list(lqw);
        dishDto.setFlavors(dishFlavorList);

        //1.3 设置categoryName数据：根据categoryId查询category表的name属性获取categoryName
        Long categoryId = dish.getCategoryId();
        Category category = categoryService.getById(categoryId);
        dishDto.setCategoryName(category.getName());

        //2. 异常处理（跟踪所有sql语句，看看哪一步会出错）。根据排查，所有语句都是查询语句，且id存在于各个表中，所以不需要异常处理
        //3. 返回数据
        return R.success(dishDto);
    }

    /**
     * 修改菜品信息与菜品对应的口味信息
     * @param dishDto 获取DTO
     * @return 返回修改成功信息
     */
    @PutMapping
    public R<String> updateDishAndFlavor(@RequestBody DishDto dishDto){

        //优化：修改菜品时删除redis缓存
        String key = "dish_" + dishDto.getCategoryId();
        redisTemplate.delete(key);

        log.info("修改菜品及其口味请求, DishDto={}", dishDto.toString());
        //1. 修改两个表，需要事务控制，转service层
        dishService.updateDishAndFlavorById(dishDto);
        //2. 返回修改成功信息
        return R.success("修改成功！");
    }

    // TODO 如果某个表要使用逻辑删除，请注意有unique约束的属性：1. 当删除某条记录时如name=zhangsan
    //                                                 2. 想要再添加name=zhangsan的记录会出现重复key异常

    /**
     * 按id删除菜品及其口味
     * @param ids id数组
     * @return 返回删除成功信息
     */
    @DeleteMapping
    public R<String> deleteById(long[] ids){ // TODO 可用数组接收url地址传参：XXX?ids=111,222,333, 也可用List<Long> ids接收
        // 优化： 删除某一菜品时删除全部redis缓存
        Set keys = redisTemplate.keys("*");
        redisTemplate.delete(keys);

        log.info("按id删除菜品，ids={}", Arrays.toString(ids));
        //1. 多表操作，定义service方法
        //2. 调用service方法
        dishService.deleteByIds(ids);
        //3. 返回删除成功信息
        return R.success("删除成功");
    }

    /**
     * 批量修改菜品售卖状态
     * @param status 接收状态参数，0禁用1启用
     * @param ids id数组
     * @return 返回成功信息
     */
    @PostMapping("/status/{status}")  // TODO Restful风格路径传参后面可跟普通参数
    public R<String> updateDishStatusByIds(@PathVariable int status, long[] ids) {
        log.info("批量修改菜品售卖状态请求，参数信息：status="+status+"，ids="+ Arrays.toString(ids));
        // 优化： 删除全部redis缓存
        Set keys = redisTemplate.keys("*");
        redisTemplate.delete(keys);

        //1. 封装修改条件
        List<Dish> dishes = new ArrayList<>();
        for (long id : ids) {
            Dish dish = new Dish();
            dish.setStatus(status);
            dish.setId(id);
            dishes.add(dish);
        }
        //2. 调用service修改
        dishService.updateBatchById(dishes); // TODO 根据id批量修改
        //3. 返回信息
        return R.success("修改成功！");
    }

    /**
     * 根据分类id获取在售菜品及其口味
     * @param categoryId 分类id
     * @return 返回菜品列表
     */
    @GetMapping("/list")
    public R<List<DishDto>> getDishByCategoryId(long categoryId){

        //优化1：判断redis中是否存在此分类的菜品，若存在则返回数据
        List<DishDto> dishDtoList = null;
        String key = "dish_" + categoryId;
        dishDtoList = (List<DishDto>) redisTemplate.opsForValue().get(key);
        if(dishDtoList != null){
            return R.success(dishDtoList);
        }

        log.info("根据分类id获取在售菜品及其口味请求，categoryId={}",categoryId);
        //1. 条件构造器
        LambdaQueryWrapper<Dish> lqw = new LambdaQueryWrapper<>();
        lqw.eq(Dish::getCategoryId, categoryId);
        lqw.eq(Dish::getStatus, 1);
        lqw.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime);

        //2. 调用service查询
        List<Dish> dishList = dishService.list(lqw);

        //3. 封装DTO
        dishDtoList = new ArrayList<>();
        // 遍历每一个dish，深拷贝其属性到dto，再设置dto的口味属性
        for (Dish dish : dishList) {
            DishDto dishDto = new DishDto();
            BeanUtils.copyProperties(dish, dishDto);
            // 设置口味属性。先根据dishId获取口味列表，再set到dot中
            LambdaQueryWrapper<DishFlavor> dishFlavorLambdaQueryWrapper = new LambdaQueryWrapper<>();
            dishFlavorLambdaQueryWrapper.eq(DishFlavor::getDishId, dish.getId());
            List<DishFlavor> dishFlavorList = dishFlavorService.list(dishFlavorLambdaQueryWrapper);
            dishDto.setFlavors(dishFlavorList);
            //添加到Dto列表
            dishDtoList.add(dishDto);
        }

        //4. 异常处理 可能某个分类无菜品（还没有添加）即dishList.size=0 但不报异常，只是返回空数据，由前端处理
        /*if (dishList.size() == 0){  TODO 1. 查询结果为空时dishList并不为空，只是size=0；2. 异常处理时若不报异常，只是返回空数据，可考虑交给前端处理
            return R.error("此分类无菜品");
        }*/
        //5. 返回成功列表数据

        //优化2：若不存在则向redis存入当前分类数据
        redisTemplate.opsForValue().set(key, dishDtoList, 60, TimeUnit.MINUTES);
        return R.success(dishDtoList);
    }

}
